using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace UtilZ.Dotnet.Ex.Model
{
    /// <summary>
    /// 资源释放器
    /// 释放顺序为降序释放,即释放顺序值越大越先释放
    /// </summary>
    public class Disposabler : IDisposable
    {
        private readonly List<IDisposablerItem> _list = new List<IDisposablerItem>();
#if NET4_0
        /// <summary>
        /// 获取释放项列表
        /// </summary>
        public List<IDisposablerItem> Items
        {
            get { return _list; }
        }
#else
        /// <summary>
        /// 获取释放项列表
        /// </summary>
        public IReadOnlyList<IDisposablerItem> Items
        {
            get { return _list; }
        }
#endif

        /// <summary>
        /// 获取已添加到释放器中的释放项数量
        /// </summary>
        public int Count
        {
            get { return _list.Count; }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public Disposabler()
        {

        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public Disposabler(IEnumerable<IDisposablerItem> disposablerItems)
        {
            if (disposablerItems != null)
            {
                foreach (var item in disposablerItems)
                {
                    if (item != null)
                    {
                        this._list.Add(item);
                    }
                }
            }
        }



        /// <summary>
        /// 分配结构体空间,并将结构体拷贝到分配的空间中,返回分配的指针
        /// </summary>
        /// <param name="obj">目标结构体对象</param>
        ///  <param name="disposeOrder">释放顺序</param>
        /// <returns>分配的指针</returns>
        public IntPtr WriteStruct<T>(ref T obj, int disposeOrder) where T : struct
        {
#if NET4_0
            var ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(T)));
#else
            var ptr = Marshal.AllocHGlobal(Marshal.SizeOf<T>());
#endif
            Marshal.StructureToPtr(obj, ptr, true);
            this._list.Add(new DisposablerItem(ptr, typeof(T), disposeOrder));
            return ptr;
        }

        /// <summary>
        /// 写结构体数组到分配结构体空间,并将结构体拷贝到分配的空间中,返回分配的指针
        /// </summary>
        /// <typeparam name="T">结构体类型</typeparam>
        /// <param name="items">数组集合</param>
        /// <param name="disposeOrder">释放顺序</param>
        /// <returns>分配的指针</returns>
        public IntPtr WriteStruct<T>(IEnumerable<T> items, int disposeOrder) where T : struct
        {
            if (items == null)
            {
                return IntPtr.Zero;
            }

            int count = items.Count();

#if NET4_0
            int structSize = Marshal.SizeOf(typeof(T));
#else
            int structSize = Marshal.SizeOf<T>();
#endif
            int totalSize = structSize * count;
            IntPtr ptr = Marshal.AllocHGlobal(totalSize);
            IntPtr structureElePtr = ptr;

            foreach (T item in items)
            {
                Marshal.StructureToPtr(item, structureElePtr, true);
                structureElePtr = structureElePtr + structSize;
            }
            this._list.Add(new DisposablerItem(ptr, disposeOrder));

            return ptr;
        }

        /// <summary>
        /// 写结构体数组,到分配结构体空间,并将结构体拷贝到分配的空间中,返回分配的指针.
        /// 在起始位置写入Int32表示的元素个数
        /// </summary>
        /// <typeparam name="T">结构体类型</typeparam>
        /// <param name="items">数组集合</param>
        /// <param name="disposeOrder">释放顺序</param>
        /// <returns>分配的指针</returns>
        public IntPtr WriteStructAttachInt32CountHead<T>(IEnumerable<T> items, int disposeOrder) where T : struct
        {
            if (items == null)
            {
                return IntPtr.Zero;
            }

#if NET4_0
            int intSize = Marshal.SizeOf(typeof(int));
            int structSize = Marshal.SizeOf(typeof(T));
#else
            int intSize = Marshal.SizeOf<int>();
            int structSize = Marshal.SizeOf<T>();
#endif
            int count = items.Count();
            int totalSize = structSize * count;
            IntPtr ptr = Marshal.AllocHGlobal(totalSize + intSize);
            Marshal.WriteInt32(ptr, count);

            IntPtr structureElePtr = ptr + intSize;

            foreach (T item in items)
            {
                Marshal.StructureToPtr(item, structureElePtr, true);
                structureElePtr = structureElePtr + structSize;
            }
            this._list.Add(new DisposablerItem(ptr, disposeOrder));

            return ptr;
        }




        /// <summary>
        /// 分配字符空间,并将字符值拷贝到分配的空间中,返回分配的指针
        /// </summary>
        /// <param name="str">目标字符串</param>
        /// <param name="disposeOrder">释放顺序</param>
        /// <param name="encoding">字符串编码方式,为null使用utf-8</param>
        /// <returns>分配的指针</returns>
        public IntPtr WriteValue(string str, int disposeOrder, Encoding encoding = null)
        {
            if (str == null)
            {
                return IntPtr.Zero;
            }

            if (encoding == null)
            {
                encoding = Encoding.UTF8;
            }
            byte[] buffer = encoding.GetBytes(str);
            var ptr = Marshal.AllocHGlobal(buffer.Length);
            this._list.Add(new DisposablerItem(ptr, disposeOrder));
            Marshal.Copy(buffer, 0, ptr, buffer.Length);
            return ptr;
        }

        /// <summary>
        /// 写byte
        /// </summary>
        /// <param name="value">要写入的值</param>
        /// <param name="disposeOrder">释放顺序</param>
        /// <returns>分配的指针</returns>
        public IntPtr WriteValue(byte value, int disposeOrder)
        {
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte)));
            this._list.Add(new DisposablerItem(ptr, disposeOrder));
            Marshal.WriteByte(ptr, value);
            return ptr;
        }

        /// <summary>
        /// 写Int16
        /// </summary>
        /// <param name="value">要写入的值</param>
        /// <param name="disposeOrder">释放顺序</param>
        /// <returns>分配的指针</returns>
        public IntPtr WriteValue(Int16 value, int disposeOrder)
        {
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int16)));
            this._list.Add(new DisposablerItem(ptr, disposeOrder));
            Marshal.WriteInt16(ptr, value);
            return ptr;
        }

        /// <summary>
        /// 写Int32
        /// </summary>
        /// <param name="value">要写入的值</param>
        /// <param name="disposeOrder">释放顺序</param>
        /// <returns>分配的指针</returns>
        public IntPtr WriteValue(Int32 value, int disposeOrder)
        {
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int32)));
            this._list.Add(new DisposablerItem(ptr, disposeOrder));
            Marshal.WriteInt32(ptr, value);
            return ptr;
        }

        /// <summary>
        /// 写Int64
        /// </summary>
        /// <param name="value">要写入的值</param>
        /// <param name="disposeOrder">释放顺序</param>
        /// <returns>分配的指针</returns>
        public IntPtr WriteValue(Int64 value, int disposeOrder)
        {
            IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Int64)));
            this._list.Add(new DisposablerItem(ptr, disposeOrder));
            Marshal.WriteInt64(ptr, value);
            return ptr;
        }

        /// <summary>
        /// 写buffer
        /// </summary>
        /// <typeparam name="T">buffer数据类型</typeparam>
        /// <param name="buffer">buffer数据</param>
        /// <param name="startIndex">buffer数据中的起始位置</param>
        /// <param name="length">要写入的数据长度</param>
        /// <param name="disposeOrder">释放顺序</param>
        /// <returns>分配的指针</returns>
        /// <exception cref="ArgumentException"></exception>
        public IntPtr WriteBuffer<T>(Array buffer, int startIndex, int length, int disposeOrder) where T : struct
        {
            Type type = typeof(T);
            int size = Marshal.SizeOf(type) * length;
            IntPtr ptr = Marshal.AllocHGlobal(size);

            TypeCode typeCode = Type.GetTypeCode(type);
            switch (typeCode)
            {
                case TypeCode.Byte:
                    Marshal.Copy((byte[])buffer, startIndex, ptr, length);
                    break;
                case TypeCode.Char:
                    Marshal.Copy((char[])buffer, startIndex, ptr, length);
                    break;
                case TypeCode.Int16:
                    Marshal.Copy((short[])buffer, startIndex, ptr, length);
                    break;
                case TypeCode.Int32:
                    Marshal.Copy((int[])buffer, startIndex, ptr, length);
                    break;
                case TypeCode.Int64:
                    Marshal.Copy((long[])buffer, startIndex, ptr, length);
                    break;
                case TypeCode.Single:
                    Marshal.Copy((float[])buffer, startIndex, ptr, length);
                    break;
                case TypeCode.Double:
                    Marshal.Copy((double[])buffer, startIndex, ptr, length);
                    break;
                default:
                    Marshal.FreeHGlobal(ptr);
                    throw new ArgumentException($"不支持的数据类型\"{type.FullName}\",只支持Marshal.Copy支持的数据类型");
            }
            this._list.Add(new DisposablerItem(ptr, disposeOrder));
            return ptr;
        }


        /// <summary>
        /// 添加IDisposable释放对象
        /// </summary>
        /// <param name="disposablerItem">释放项</param>
        public void Add(IDisposablerItem disposablerItem)
        {
            if (disposablerItem != null)
            {
                this._list.Add(disposablerItem);
            }
        }

        /// <summary>
        /// 添加IDisposable释放对象
        /// </summary>
        /// <param name="disposable">IDisposable释放对象</param>
        ///  <param name="disposeOrder">释放顺序,值越大,越先释放</param>
        public void Add(IDisposable disposable, int disposeOrder)
        {
            if (disposable == null)
            {
                throw new ArgumentNullException(nameof(disposable));
            }

            this._list.Add(new DisposablerItem(disposable, disposeOrder));
        }

        /// <summary>
        /// 添加指针
        /// </summary>
        /// <param name="ptr">void* 指针</param>
        ///  <param name="disposeOrder">释放顺序,值越大,越先释放</param>
        unsafe public void Add(void* ptr, int disposeOrder)
        {
            if (ptr == null)
            {
                return;
            }

            this._list.Add(new DisposablerItem(ptr, disposeOrder));
        }

        /// <summary>
        /// 添加指针
        /// </summary>
        /// <param name="ptr">指针</param>
        ///  <param name="disposeOrder">释放顺序,值越大,越先释放</param>
        public void Add(IntPtr ptr, int disposeOrder)
        {
            if (ptr == IntPtr.Zero)
            {
                return;
            }

            this._list.Add(new DisposablerItem(ptr, disposeOrder));
        }

        /// <summary>
        /// 添加结构体指针
        /// </summary>
        /// <param name="ptr">结构体指针</param>
        /// <param name="type">结构体类型</param>
        ///  <param name="disposeOrder">释放顺序,值越大,越先释放</param>
        /// <exception cref="ArgumentNullException">type为null抛出异常</exception>
        public void Add(IntPtr ptr, Type type, int disposeOrder)
        {
            if (ptr == IntPtr.Zero)
            {
                return;
            }

            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            this._list.Add(new DisposablerItem(ptr, type, disposeOrder));
        }

        /// <summary>
        /// 添加结构体指针
        /// </summary>
        /// <typeparam name="T">结构体类型</typeparam>
        /// <param name="ptr">结构体指针</param>
        ///  <param name="disposeOrder">释放顺序,值越大,越先释放</param>
        public void Add<T>(IntPtr ptr, int disposeOrder) where T : struct
        {
            if (ptr == IntPtr.Zero)
            {
                return;
            }

            this._list.Add(new DisposablerItem(ptr, typeof(T), disposeOrder));
        }





        /// <summary>
        /// 释放已添加的指针并重置对象
        /// </summary>
        public void Reset()
        {
            this.PrimitiveDispose();
            this._disposed = false;
        }


        private bool _disposed = false;
        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            if (this._disposed)
            {
                return;
            }
            this._disposed = true;

            this.PrimitiveDispose();
        }

        private void PrimitiveDispose()
        {
            var disArr = this._list.OrderByDescending(t =>
            {
                return t.DisposeOrder;
            }).ToArray();

            foreach (var dis in disArr)
            {
                dis.Dispose();
            }
            this._list.Clear();
        }

    }

}
